/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gblend.frequentindex;

/**
 *
 * @author cjjin
 */

/*
 * Copyright 2010, Center for Advanced Information Systems,Nanyang Technological University
 *
 * File name: BuildFreqIndex.java
 *
 * Abstract: Build the frequent fragments into the A2F-index
 *
 * Current Version:      0.1
 * Auther:               Jin Changjiu
 * Modified Date:        Mar.5, 2010
 *
 */
import gblend.adjlistgraph.ALGraph;
import gblend.adjlistgraph.FsgIdEntry;
import gblend.db.Parameters;
import gblend.exactquery.NewUllmanVerify;
import gblend.infrequentindex.BuildInfreqIndex;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
import gblend.util.RunTime;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;

public class BuildFreqIndex {

    private static Vector<ALGraph> freGraphlist = new Vector<ALGraph>();
    private static Vector<ALGraph> sindex = new Vector<ALGraph>();
    private static FGraph cindex = null;
    private static RunTime time = new RunTime();
    private static int fsize;
    private static NewUllmanVerify uv = new NewUllmanVerify();

    //verify the subgraph relationship
    public static boolean subgraphVerify(int m, int n) {
        //    System.out.println(">>>IN BuidFreqIndex:subgraphVerify");
        return uv.verify(freGraphlist.elementAt(m), freGraphlist.elementAt(n));

    }

    public static void buildcluster(Cluster cluster, int id) {
        System.out.println(">>>IN BuidFreqIndex:buildcluster");
        int iid = cluster.getNodeset().indexOf(id);

        ALGraph snode = sindex.elementAt(id - fsize);
        for (int i = 0; i < snode.getChildren().size(); i++) {
            int newid = snode.getChildren().elementAt(i);
            if (cluster.getNodeset().contains(newid) == false) {
                cluster.setNode(newid);
            }

            int jid = cluster.getNodeset().indexOf(newid);

            if (cluster.getEdges().contains(iid + " " + jid) == false) {
                cluster.setEdge(iid, jid);
            }

            buildcluster(cluster, newid);

        }


    }

    //A2F-index building process
    public static long build(BuildInfreqIndex bif, Parameters para) throws SQLException, ClassNotFoundException {
        System.out.println(">>>IN BuidFreqIndex:build");
        int b = para.getb();
        String as = Float.toString(para.geta()).substring(0, 5);
        //Goto the location of the database file on local drive
        String dir = "data/" + para.getName() + "/" + para.getdatasize() + "k/"
                + as + para.getName() + para.getdatasize() + "k" + b;

        String output1 = dir + "DFIndex";
        String output2 = dir + "MFIndex";

        PrintStream p = null, q = null;
        File fout1 = new File(output1);
        File fout2 = new File(output2);

        try {

            time.time[RunTime.RUN_START] = System.currentTimeMillis();
            //read or construct the A2I-index
            bif.readInfrequents(para);

            System.out.println("Number of fragments in A2I-index: " + bif.getInfGraph().size());

            if (fout2.exists())//if MF-index exists, read it
            {
                readMFindex(fout2);
            } else //build MF-index
            {
                //build MF-index
                BufferedOutputStream bos2 = new BufferedOutputStream(new FileOutputStream(fout2));
                q = new PrintStream(bos2);
                //build DF-index
                BufferedOutputStream bos1 = new BufferedOutputStream(new FileOutputStream(fout1));
                p = new PrintStream(bos1);

                if (bif.getfreqgraphs().isEmpty()) {
                    bif.readFrequents(para,false);
                }
                freGraphlist = bif.getfreqgraphs();

                for (fsize = 0; fsize < freGraphlist.size(); fsize++) {
                    if (freGraphlist.elementAt(fsize).getEnum() > b) {
                        break;
                    }
                }
                /*
                //COUNT the freq frag that has 1 edge
                int k=0;
                for(k=0;k<freGraphlist.size();k++){
                if(freGraphlist.elementAt(k).getEnum()>=2){
                break;
                }
                }*/

                cindex = new FGraph(fsize);
                q.println(fsize);//the node size(#of fragments) in MF-index

                HashSet<Integer> clusterset = new HashSet<Integer>();// the first node of each cluster

                for (int i = 0; i < freGraphlist.size(); i++) {
                    ALGraph fnodei = freGraphlist.elementAt(i);

                    if (fnodei.getEnum() != b) {
                        for (int j = i + 1; j < freGraphlist.size(); j++)//j is all the children of i
                        {
                            ALGraph fnodej = freGraphlist.elementAt(j);
                            if ((fnodei.getEnum() == fnodej.getEnum() - 1) && subgraphVerify(i, j)) {
                                if (j < fsize) {
                                    cindex.addEdge(i, j);
                                }
                                fnodei.setChildren(j);
                                if (fnodei.getidlist().size() == 0) {
                                    System.out.println("ERRORRRRR: A ZERO ID LIST HERE j=" + j + " i=" + i);
                                }
                                fnodei.getidlist().removeAll(fnodej.getidlist());

                            }
                        }

                        if (fnodei.getEnum() < b)// for node in c-index
                        {
                            q.print(fnodei.getCam() + " :");
                            //set new id list
                            // Vector<FsgIdEntry> fni = new Vector<FsgIdEntry>();
                            //  Collections.sort(fni);
                            Iterator<FsgIdEntry> itr = fnodei.getidlist().iterator();
                            if (itr.hasNext()) {
                                FsgIdEntry id = itr.next();
                                Vector<Integer> idl = id.getGraphId();
                                int idlsize = idl.size();
                                for (int j = 0; j < idlsize; j++) {
                                    q.print(" " + idl.elementAt(j).toString());
                                }
                            }
                            /*  if (id.isAtBoundary()) {
                            q.print(" 1");
                            } else {
                            q.print(" 0");
                            }*/
                            while (itr.hasNext()) {
                                q.print(" ,");
                                FsgIdEntry id = itr.next();
                                Vector<Integer> idl = id.getGraphId();
                                int idlsize = idl.size();
                                for (int j = 0; j < idlsize; j++) {
                                    q.print(" " + idl.elementAt(j).toString());
                                }
                                /*if (id.isAtBoundary()) {
                                q.print(" 1");
                                } else {
                                q.print(" 0");
                                }*/
                            }
                            q.println();
                            cindex.addnode(fnodei);
                        } else if (fnodei.getEnum() > b && p != null)//for node in s-index
                        {
                            p.print(" " + i + ":");
                            //  Vector<FsgIdEntry> fni = new Vector<FsgIdEntry>();
                            //  Collections.sort(fni);
                            Iterator<FsgIdEntry> itr = fnodei.getidlist().iterator();
                            if (itr.hasNext()) {
                                FsgIdEntry id = itr.next();
                                Vector<Integer> idl = id.getGraphId();
                                int idlsize = idl.size();
                                for (int j = 0; j < idlsize; j++) {
                                    p.print(" " + idl.elementAt(j));
                                }
                            }
                            /*if (id.isAtBoundary()) {
                            p.print(" 1");
                            } else {
                            p.print(" 0");
                            }*/
                            while (itr.hasNext()) {
                                p.print(",");
                                FsgIdEntry id = itr.next();
                                Vector<Integer> idl = id.getGraphId();
                                for (int j = 0; j < idl.size(); j++) {
                                    p.print(" " + idl.elementAt(j));
                                }
                                /*if (id.isAtBoundary()) {
                                p.print(" 1");
                                } else {
                                p.print(" 0");
                                }*/
                            }
                            p.println();
                            sindex.addElement(fnodei);

                        }

                    } else // edge == b
                    {
                        q.print(fnodei.getCam() + " :");
                        // Vector<FsgIdEntry> fni = new Vector<FsgIdEntry>();
                        //      Collections.sort(fni);
                        Iterator<FsgIdEntry> itr = fnodei.getidlist().iterator();
                        if (itr.hasNext()) {
                            FsgIdEntry id = itr.next();
                            Vector<Integer> idl = id.getGraphId();
                            int idlsize = idl.size();
                            for (int j = 0; j < idlsize; j++) {
                                q.print(" " + idl.elementAt(j).toString());
                            }
                        }
                        /*if (id.isAtBoundary()) {
                        q.print(" 1");
                        } else {
                        q.print(" 0");
                        }*/
                        while (itr.hasNext()) {
                            q.print(" ,");
                            FsgIdEntry id = itr.next();
                            Vector<Integer> idl = id.getGraphId();
                            for (int j = 0; j < idl.size(); j++) {
                                q.print(" " + idl.elementAt(j).toString());
                            }
                            /*if (id.isAtBoundary()) {
                            q.print(" 1");
                            } else {
                            q.print(" 0");
                            }*/
                        }

                        q.println();

                        for (int j = i + 1; j < freGraphlist.size(); j++)//j is all the children of i
                        {
                            ALGraph fnodej = freGraphlist.elementAt(j);
                            if ((fnodei.getEnum() == fnodej.getEnum() - 1) && subgraphVerify(i, j)) {
                                fnodei.setClusters(j);
                                clusterset.add(j);
                                cindex.setClusterHeads(j, fnodej.getCam());
                            }
                        }
                        cindex.addnode(fnodei);
                    }
                }

                //print C-index edges
                for (int m = 0; m < fsize; m++) {
                    for (int n = m; n < fsize; n++) {
                        if (cindex.getEdge(m, n) == 1) {
                            q.println(m + " " + n);
                        }
                    }
                    //out put cluster information
                    if (cindex.getNode(m).getEnum() == b && !cindex.getNode(m).getClusters().isEmpty()) {
                        q.print(m + " ;");
                        for (int t = 0; t < cindex.getNode(m).getClusters().size(); t++) {
                            q.print(" " + cindex.getNode(m).getClusters().elementAt(t));
                        }
                        q.println();

                    }
                }
                //out put the cluster set
                Hashtable<Integer, String> clusterCamSet = new Hashtable<Integer, String>();
                clusterCamSet = cindex.getClusterHeads();
                for (Iterator itr = clusterCamSet.keySet().iterator(); itr.hasNext();) {
                    int cid = (Integer) itr.next();
                    q.println(cid + " | " + clusterCamSet.get(cid));
                }

                if (p != null) {
                    //construct the clusters
                    List<Integer> list = new ArrayList<Integer>();

                    for (final Integer value : clusterset) {
                        list.add(value);
                    }

                    Collections.sort(list);

                    for (Iterator itr = list.iterator(); itr.hasNext();) {
                        int id = (Integer) itr.next();
                        Cluster cluster = new Cluster();
                        cluster.setNode(id);
                        // cluster building
                        buildcluster(cluster, id);

                        //output
                        p.println("Cluster " + id);
                        p.println(cluster.getNodeset().size());
                        for (int i = 0; i < cluster.getNodeset().size(); i++) {
                            int gid = cluster.getNodeset().elementAt(i);
                            p.println(freGraphlist.elementAt(gid).getCam() + ":" + gid);
                        }

                        for (int i = 0; i < cluster.getEdges().size(); i++) {
                            p.println(cluster.getEdges().elementAt(i));

                        }

                        p.println("End");

                    }

                    clusterset.clear();
                    list.clear();
                    p.flush();
                    p.close();

                }
                //clear the frequent fragments set
                freGraphlist.clear();

                //Close the input stream
                q.flush();
                q.close();
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        time.time[RunTime.RUN_END] = System.currentTimeMillis();
        time.time[RunTime.USED] = time.time[RunTime.RUN_END] - time.time[RunTime.RUN_START];
        System.out.println("Number of fragments in MF-index: " + cindex.getSize()
                + " Indices Building Time(s): " + time.time[RunTime.USED] / 1000);
        return time.time[RunTime.USED];
    }

    //read MF-index to memory
    public static FGraph readMFindex(File fin) throws FileNotFoundException, IOException {
        System.out.println(">>>IN BuidFreqIndex:readMFindex");
        String strLine = null;
        BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fin));
        BufferedReader br = new BufferedReader(new InputStreamReader(bin));

        int nodenum = -1;
        String firstLine = br.readLine();
        cindex = new FGraph(Integer.parseInt(firstLine));

        while ((strLine = br.readLine()) != null) {
            if (strLine.contains(":")) {
                ALGraph node = new ALGraph(0);//set default size 0
                nodenum++;
                String[] list = strLine.split(":");
                node.setCam(list[0].trim());
                node.setGraphid(nodenum);
                if (list.length > 1) {
                    String[] list1 = list[1].trim().split(",");

                    for (int j = 0; j < list1.length; j++) {
                        String[] list2 = list1[j].trim().split("\\s");
                        FsgIdEntry tmpe = new FsgIdEntry();
                        int size = list2.length;
                        for (int k = 0; k < size; k++) {
                            tmpe.addGraphId(Integer.parseInt(list2[k]));
                        }
                        /*if (Integer.parseInt(list2[size]) != 0) {
                        tmpe.setAtBoundary(true);
                        }*/
                        node.setidlist(tmpe);
                    }
                }
                cindex.addnode(node);

            } else if (!strLine.contains(";") && !strLine.contains("|")) {
                String[] pair = strLine.split("\\s");
                int src = Integer.parseInt(pair[0]);
                int trg = Integer.parseInt(pair[1]);
                cindex.addEdge(src, trg);
                cindex.getNode(src).setChildren(trg);//set children
            } else if (strLine.contains(";"))//read the clusters
            {
                String[] list = strLine.split("\\s");
                for (int j = 2; j < list.length; j++) {
                    cindex.getNode(Integer.parseInt(list[0])).setClusters(Integer.parseInt(list[j]));
                }
            } else if (strLine.contains("|"))//read the clusters
            {
                String[] list = strLine.split("\\s");
                for (int j = 2; j < list.length; j++) {
                    cindex.setClusterHeads(Integer.parseInt(list[0]), list[2]);
                }
            }

        }
        //Close the input stream
        br.close();

        return cindex;
    }

    public FGraph getFreqGraph() {
        System.out.println(">>>IN BuidFreqIndex:getFreqGraph");
        return cindex;
    }
}

